package com.yanqu.road.server.channelserver.manager.user;

import com.yanqu.road.entity.player.ChannelUserBaseInfo;
import com.yanqu.road.entity.player.CrossUserBaseInfo;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.logic.bussiness.player.ChannelUserBaseInfoBusiness;
import com.yanqu.road.server.TempMgr;
import com.yanqu.road.server.manger.IUserBaseInfoSyncListener;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class ChannelUserMgr extends TempMgr {

    /**
     * k:UserId
     */
    private static Map<Long, ChannelUserBaseInfo> userBaseInfoMap;

    /**
     * k:serverId  k: userAliasId   userAliasId对应的 ChannelUserBaseInfo
     */
    private static Map<Long, Map<Long, ChannelUserBaseInfo>> userAliasIdUserBaseInfoMap = new ConcurrentHashMap<>();

    private static Set<Long> selectDbUserIdList = new HashSet<>();

    private static Map<Long, List<Long>> userChannelCacheMap = new ConcurrentHashMap<>();

    public static List<ChannelUserBaseInfo> getChannelUserBaseInfoList() {
        return new ArrayList<>(userBaseInfoMap.values());
    }

    public static void updateUserBaseInfo(long userId, UserBaseInfo userBaseInfo) {
        if (userBaseInfo == null) {
            return;
        }
        synchronized (userBaseInfoMap) {
            ChannelUserBaseInfo info = userBaseInfoMap.get(userId);
            if (info == null) {
                info = new ChannelUserBaseInfo();
                info.setUserId(userId);
                info.setLastUpdateTime(System.currentTimeMillis());
                info.setUserBaseInfo(userBaseInfo);
                info.setInsertOption();
                userBaseInfoMap.put(userId, info);
                Map<Long, ChannelUserBaseInfo> map = getUserAliasIdUserBaseInfoMap(userBaseInfo.getServerId(), true);
                map.put(userBaseInfo.getUserAliasId(), info);
            } else {
                info.setUserBaseInfo(userBaseInfo);
                info.setLastUpdateTime(System.currentTimeMillis());
            }
        }
    }

    //加个是否被修改的标志，要实时更新的,目前就判断一下外观
    public static boolean updateUserBaseInfoChange(long userId, UserBaseInfo userBaseInfo) {
        if (userBaseInfo == null) {
            return false;
        }
        synchronized (userBaseInfoMap) {
            ChannelUserBaseInfo info = getChannelUserBaseInfo(userId);
            if (info == null) {
                info = new ChannelUserBaseInfo();
                info.setUserId(userId);
                info.setLastUpdateTime(System.currentTimeMillis());
                info.setUserBaseInfo(userBaseInfo);
                info.setInsertOption();
                userBaseInfoMap.put(userId, info);
                Map<Long, ChannelUserBaseInfo> map = getUserAliasIdUserBaseInfoMap(userBaseInfo.getServerId(), true);
                map.put(userBaseInfo.getUserAliasId(), info);
                return true;
            } else {
                boolean change = false;
                if (!info.getUserBaseInfo().getDecoration().equals(userBaseInfo.getDecoration())
                        || !info.getUserBaseInfo().getNickName().equals(userBaseInfo.getNickName())
                ) {
                    change = true;
                }
                info.setUserBaseInfo(userBaseInfo);
                info.setLastUpdateTime(System.currentTimeMillis());
                return change;
            }
        }
    }

    public static ChannelUserBaseInfo getChannelUserBaseInfo(long userId) {
        ChannelUserBaseInfo ChannelUserBaseInfo = userBaseInfoMap.get(userId);
        if (ChannelUserBaseInfo == null) {
            synchronized (userBaseInfoMap) {
                ChannelUserBaseInfo = userBaseInfoMap.get(userId);
                if (ChannelUserBaseInfo == null && !selectDbUserIdList.contains(userId)) {
                    ChannelUserBaseInfo = ChannelUserBaseInfoBusiness.getUserBaseInfoByUserId(userId);
                    if (ChannelUserBaseInfo != null) {
                        ChannelUserBaseInfo.setLastUpdateTime(System.currentTimeMillis());
                        userBaseInfoMap.put(userId, ChannelUserBaseInfo);
                        UserBaseInfo userBaseInfo = ChannelUserBaseInfo.getUserBaseInfo();
                        Map<Long, ChannelUserBaseInfo> map = getUserAliasIdUserBaseInfoMap(userBaseInfo.getServerId(), true);
                        map.put(userBaseInfo.getUserAliasId(), ChannelUserBaseInfo);
                    }
                    selectDbUserIdList.add(userId);
                }
            }
        }
        return ChannelUserBaseInfo;
    }

    public static UserBaseInfo getUserBaseInfo(long relationshipUserId) {
        ChannelUserBaseInfo ChannelUserBaseInfo = getChannelUserBaseInfo(relationshipUserId);
        if (ChannelUserBaseInfo == null) {
            return null;
        }
        return ChannelUserBaseInfo.getUserBaseInfo();
    }

    /**
     * 通过玩家id  获取区服id
     *
     * @param userId
     * @return
     */
    public static long getServerIdByUserId(long userId) {
        //先拿缓存
        if (userChannelCacheMap.containsKey(userId)) {
            List<Long> longList = userChannelCacheMap.get(userId);
            if (longList != null && longList.size() == 2) {
                return longList.get(1);
            }
        }
        //拿不到再去拿baseInfo
        ChannelUserBaseInfo channelUserBaseInfo = getChannelUserBaseInfo(userId);
        if (channelUserBaseInfo != null) {
            return channelUserBaseInfo.getUserBaseInfo().getServerId();
        }
        return 0;
    }

    // 赶时间乱写的，没事别用这个
    @Deprecated
    public static ChannelUserBaseInfo getMemUserBaseInfo(long userId) {
        return userBaseInfoMap.get(userId);
    }


    /**
     * 获取玩家名称
     *
     * @param userId
     * @return
     */
    public static String getUserName(long userId) {
        ChannelUserBaseInfo userBaseInfo = getChannelUserBaseInfo(userId);
        if (userBaseInfo == null) {
            return "";
        }
        return userBaseInfo.getUserBaseInfo().getNickName();
    }

    @Override
    public boolean reloadData() throws Exception {
        Map<Long, ChannelUserBaseInfo> tempMap = new ConcurrentHashMap<>();
//        Map<Long, ChannelUserBaseInfo> allUserBaseInfo = ChannelUserBaseInfoBusiness.getAllUserBaseInfo();
        long startTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L);
        Map<Long, ChannelUserBaseInfo> allUserBaseInfo = ChannelUserBaseInfoBusiness.getUserBaseInfoUpdateInTime(startTime);
        tempMap.putAll(allUserBaseInfo);
        userBaseInfoMap = tempMap;
        for (ChannelUserBaseInfo item : allUserBaseInfo.values()) {
            Map<Long, ChannelUserBaseInfo> map = getUserAliasIdUserBaseInfoMap(item.getUserBaseInfo().getServerId(), true);
            map.put(item.getUserBaseInfo().getUserAliasId(), item);
        }

        return true;
    }

    public static Map<Long, ChannelUserBaseInfo> getUserAliasIdUserBaseInfoMap(long serverId, boolean newIfNull) {
        Map<Long, ChannelUserBaseInfo> map = userAliasIdUserBaseInfoMap.get(serverId);
        if (map == null && newIfNull) {
            map = new ConcurrentHashMap<>();
            userAliasIdUserBaseInfoMap.put(serverId, map);
        }
        return map;
    }

    public static long getChannelUserBaseInfoModiftyTime(long userId) {
        long modifyTime = 0;
        ChannelUserBaseInfo ChannelUserBaseInfo = getChannelUserBaseInfo(userId);
        if (null != ChannelUserBaseInfo) {
            modifyTime = ChannelUserBaseInfo.getLastUpdateTime();
        }
        return modifyTime;
    }

    @Override
    public boolean save() {
        removeOverTimeData();
        for (ChannelUserBaseInfo baseInfo : new ArrayList<>(userBaseInfoMap.values())) {
            if (baseInfo.isDirty()) {
                ChannelUserBaseInfoBusiness.update(baseInfo);
            }
        }
        return true;
    }

    private void removeOverTimeData() {
        long startTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L);
        List<ChannelUserBaseInfo> needRemoveUserList = new ArrayList<>();
        for (ChannelUserBaseInfo ChannelUserBaseInfo : userBaseInfoMap.values()) {
            if (ChannelUserBaseInfo.getLastUpdateTime() < startTime) {
                needRemoveUserList.add(ChannelUserBaseInfo);
            }
            if (needRemoveUserList.size() > 300000) {
                break;
            }
        }

        if (needRemoveUserList.size() > 0) {
            getLogger().debug("remove user info {}", needRemoveUserList.size());
            for (ChannelUserBaseInfo userBaseInfo : needRemoveUserList) {
                synchronized (userBaseInfoMap) {
                    Map<Long, ChannelUserBaseInfo> serverUserBaseInfoMap = userAliasIdUserBaseInfoMap.get(userBaseInfo.getUserBaseInfo().getServerId());
                    if (serverUserBaseInfoMap != null) {
                        serverUserBaseInfoMap.remove(userBaseInfo.getUserBaseInfo().getUserAliasId());
                    }
                    userBaseInfoMap.remove(userBaseInfo.getUserId());
                    selectDbUserIdList.remove(userBaseInfo.getUserId());
                }
            }
        }
    }

    @Override
    public boolean init() throws Exception {
        reload();
        return true;
    }

    @Override
    public boolean reloadConfig() throws Exception {
        return true;
    }

    @Override
    public boolean stop() {
        return false;
    }


    public class SyncHandle implements IUserBaseInfoSyncListener {
        @Override
        public void notifySync(UserBaseInfo userBaseInfo, long userId) {
            updateUserBaseInfo(userId, userBaseInfo);
        }
    }


    /**
     * 用CrossChannelCommand接收到的请求,都会缓存起来
     * @param userId
     * @param serverId
     * @param channelId
     */
    public static void cacheUserChannel(long userId, long serverId, int channelId) {
        if (userChannelCacheMap.containsKey(userId)) {
            return;
        }
        List<Long> list = new ArrayList<>();
        list.add((long) channelId);
        list.add(serverId);
        userChannelCacheMap.put(userId, list);
    }

    /**
     * 拿玩家id对应的渠道id和区服id
     * @param userId
     * @return
     */
    public static List<Long> getUserCacheChannel(long userId) {
        return userChannelCacheMap.get(userId);
    }
}
